using System;
using System.Drawing;

namespace PacSnake
{
	/// <summary>
	/// The Level class holds all the level information, draws the level to the
	/// screen and checks if there is food or walls where the snake goes.
	/// </summary>
	public class Level
	{
	    // Level name
		private string name = string.Empty;

        // Level width and height
		private int width = 0;
		private int height = 0;
		
		// Start position of the snake in the level
        private int snakePosX = 0;
        private int snakePosY = 0;
		
		// String representation the the level as stored in the XML
		private string levelMap = string.Empty;
		
		// The 'layout' of the level, consisting of walls, etc...
		private Item[,] itemsInLevel;
		
        // Offsets to center the level in the middle of the screen
        private int mazeOffsetX = 0;
        private int mazeOffsetY = 0;
        
        // Pieces of food in the level
        private int nrOfCandy = 0;
        
        // Speed of the snake in the level (in ms.)
        private int speed = 200;
        
        // For drawing the level on screen
        private Bitmap img;
        private Graphics g;

        
        #region Constructor
        
        /// <summary>
        /// Constructor: Sets the level information.
        /// </summary>
        /// <param name="aName">Level name</param>
        /// <param name="aLevelMap">String representation of the level</param>
        /// <param name="aWidth">Level width</param>
        /// <param name="aHeight">Level height</param>
        /// <param name="aSpeed">Level speed</param>
        public Level(string aName, string aLevelMap, int aWidth, int aHeight,
            int aSpeed)
        {
            name = aName;
            levelMap = aLevelMap;
            width = aWidth;
            height = aHeight;
            speed = aSpeed;
        }
        
        #endregion
        
        #region Generating and drawing the level
        
        /// <summary>
        /// Set the offset so that the level will always be centered, and so
        /// that the largest possible level still has a small edge around it.
        /// </summary>
        public void SetOffsets()
        {
            mazeOffsetX = (int)
                ((((double)30 - (double)width) / 2) * 20) + 10;
            mazeOffsetY = (int)
                ((((double)25 - (double)height) / 2) * 20) + 10;
        }
        
        
        /// <summary>
        /// Reads the level string character by character and stores an Item
        /// object in level layout. It also counts the number of foods in the
        /// level and it sets the position of the snake.
        /// </summary>
        public void GenerateLevel() 
        {
            char[] cMaze = levelMap.ToCharArray();
            int c = 0;
            nrOfCandy = 0;

            itemsInLevel = new Item[width, height];

            for (int i = 0; i < height; i++) 
            {
                for (int j = 0; j < width; j++) 
                {
                    itemsInLevel[j, i] =
                        new Item(cMaze[c], j, i, mazeOffsetX, mazeOffsetY);
                    
                    if (cMaze[c] == 'F')
                        nrOfCandy++;
                    else if (cMaze[c] == 'S')
                    {
                        snakePosX = j + 1;
                        snakePosY = i + 1;
                    }
                    c++;
                }
            }
        }
        
        
        /// <summary>
        /// This method draws the level. It takes each item in the level and
        /// draws the correspoding image.
        /// </summary>
        /// <param name="levelWidth">Level width</param>
        /// <param name="levelHeight">Level height</param>
        /// <param name="skin">Skin object</param>
        /// <returns>The Bitmap of the level</returns>
        public Bitmap Draw(int levelWidth, int levelHeight, Skin skin)
        {
            img = new Bitmap(levelWidth, levelHeight);
            g = Graphics.FromImage(img);
            
            g.DrawImage(skin.Background, 0, 0);

            // Draw the level
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    Image image = GetLevelImage(itemsInLevel[i, j], skin);

                    // No free space
                    if (image != null)
                        g.DrawImage(image, mazeOffsetX + i * 20,
                            mazeOffsetY + j * 20, 20, 20);
                    
                    Item item = itemsInLevel[i, j];
                    if (item.IsCandy && !item.IsCandyEaten)
                        g.DrawImage(skin.Food, item.Rect.X, item.Rect.Y);
                }
            }
            
            return img;
        }
        
        #endregion
        
        #region Checking for walls or food
        
        /// <summary>
        /// Checks if there is a wall below, above, on the left or on the right
        /// of the snake, depending on the direction of the snake. The offsets
        /// are relative to the snake's head and contain the item in the level
        /// that we want to check if it's a wall or not.
        /// </summary>
        /// <param name="xOff">X-offset</param>
        /// <param name="yOff">Y-offset</param>
        /// <param name="snake">'Rectangle' head of the snake</param>
        /// <returns>True if there's a wall, otherwise false</returns>
        public bool IsWallThere(int xOff, int yOff, Rectangle snake)
        {
            snake.X += xOff;
            snake.Y += yOff;
            
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                    if (itemsInLevel[i,j].IsWall)
                        if (snake.IntersectsWith(itemsInLevel[i,j].Rect))
                            return true;

            return false;
        }
        
        
        /// <summary>
        /// Check if there is food on the position of the head of the snake.
        /// If so, mark the food as eaten so it won't be displayed again.
        /// </summary>
        /// <param name="snake">'Rectangle' head of the snake</param>
        /// <returns>True if there's food, otherwise false</returns>
        public bool CheckFood(Rectangle snake)
        {
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                    if (itemsInLevel[i,j].IsCandy &&
                        !itemsInLevel[i,j].IsCandyEaten)
                    {
                        if (snake.IntersectsWith(itemsInLevel[i,j].Rect))
                        {
                            nrOfCandy--;
                            itemsInLevel[i,j].IsCandyEaten = true;
                            return true;
                        }
                    }

            return false;
        }
        
        #endregion
        
        #region Getting the level images
        
        /// <summary>
        /// Gets the level image, depending of the item in the level
        /// </summary>
        /// <param name="item">Item object</param>
        /// <param name="skin">Skin object</param>
        /// <returns>Corresponding image</returns>
        private Image GetLevelImage(Item item, Skin skin)
        {
            Image image;
            
		    switch (item.ItemType)
		    {
                case ItemType.CornerCross:
                    image = skin.WallCross;
                    break;
                case ItemType.CornerLowerLeft:
                    image = skin.WallCornerLL;
                    break;
                case ItemType.CornerLowerRight:
                    image = skin.WallCornerLR;
                    break;
                case ItemType.CornerUpperLeft:
                    image = skin.WallCornerUL;
                    break;
                case ItemType.CornerUpperRight:
                    image = skin.WallCornerUR;
                    break;
                case ItemType.CornerTDown:
                    image = skin.WallTDown;
                    break;
                case ItemType.CornerTLeft:
                    image = skin.WallTLeft;
                    break;
                case ItemType.CornerTRight:
                    image = skin.WallTRight;
                    break;
                case ItemType.CornerTUp:
                    image = skin.WallTUp;
                    break;
                case ItemType.StraightHorizontal:
                    image = skin.WallStraightH;
                    break;
                case ItemType.StraightVertical:
                    image = skin.WallStraightV;
                    break;
		        default:
                    image = null;
                    break;
		    }
		    
		    if (item.IsWall)
		        return image;
		    else
		        return null;
        }
        
        #endregion
        
        #region Properties
		
        public string Name
        {
            get { return name; }
        }
        
        public int Width
        {
            get { return width; }
        }
        
        public int Height
        {
            get { return height; }
        }
        
        public int MazeOffsetX
        {
            get { return mazeOffsetX; }
        }
        
        public int MazeOffsetY
        {
            get { return mazeOffsetY; }
        }
        
        public int NrOfCandy
        {
            get { return nrOfCandy; }
        }
        
        public int Speed
        {
            get { return speed; }
        }
        
        public int SnakePosX
        {
            get { return snakePosX; }
            set { snakePosX = value; }
        }
        
        public int SnakePosY
        {
            get { return snakePosY; }
            set { snakePosY = value; }
        } 
        
        public Item[,] ItemsInLevel
        {
            get { return itemsInLevel; }
        }
        
        #endregion
	}
}
